# -*- coding: utf-8 -*-
import webbrowser
import sys
import threading, pyperclip
import tkinter as tk
from tkinter import messagebox
import hardware as Hardware
from benchmarks.benchmark import Benchmark
from tkutil.ImageHelp import ImageHelp
from tkutil.TkMove import TkMove
import tkutil.FontUtil as FontUtil
import tkutil.VersionUtil as VersionUtil
from service import api
import logging
import traceback

bg = '#01131E'
bg_active = '#0A3544'
bg_line = '#0A3544'
COLOR1 = '#2FBEDA'
COLOR2 = '#E6E6E6'
COLOR3 = '#FFFFFF'
PRODUCT_ABS_TAG = False

GUIDE_URL = 'https://uploads-ssl.webflow.com/5f345a660c5d7cfdea4f8725/60903137deee2f75bf025fdc_ABS-Quick%20Start%20guide-web.pdf'
FAQ_URL = 'https://www.abs.com/faq'
TWTTER_URL = 'https://twitter.com/ABSGamingPC'
INSTAGRAM_URL = 'https://instagram.com/ABSGamingPC'


class Application(tk.Tk):

    def __init__(self):
        super().__init__()
        logging.info("eniac start")
        FontUtil.loadfont('assets/font/OpenSans-Bold.ttf')
        FontUtil.loadfont('assets/font/OpenSans-Regular.ttf')
        self.version = "1.0.0"
        self.title("ENICA E-SCORE")
        self.configure(bg=bg)
        logging.info("load hardware")
        self.hardware = Hardware.getAll()
        logging.info(self.hardware)
        self.benchmark = {'cpu': None, 'memory': None, 'disk': None, 'gpu': None, 'score': 'Loading...'}
        self.global_score_frame = None
        self.cpu_score_frame = None
        self.gpu_score_frame = None
        self.mem_score_frame = None
        self.disk_score_frame = None
        self.initTitle()
        self.initScoreFrame()
        self.initHardwareFrame()
        self.findScore()
        self.versionCheck()
        TkMove(self, "assets/img/icon.ico")

    def initTitle(self):
        title = tk.Frame(self, bg=bg, height=30)
        title.pack(side='top', fill='x')
        tk.Label(title, image=ImageHelp.getIcons('logo', (90, 45)), bg=bg).pack(side='left', padx=15, pady=15)

        buttons_frame = tk.Frame(title, bg=bg)
        self.addProductKeyButton(buttons_frame)
        if PRODUCT_ABS_TAG:
            self.addTwtterButton(buttons_frame)
            self.addInstagramButton(buttons_frame)
            self.addGuideButton(buttons_frame)
            self.addFaqButton(buttons_frame)
        self.addShareButton(buttons_frame)
        self.addCloseButton(buttons_frame)
        buttons_frame.pack(side='right', anchor='ne')

    def addProductKeyButton(self, buttons_frame):
        button = tk.Menubutton(buttons_frame, compound='left', cursor="hand2", image=ImageHelp.getIcons('key', (18, 18)),
                                          activebackground=bg_active, bg=bg, fg=COLOR2)
        button.bind("<ButtonRelease-1>", lambda e: self.showProcudtKey())
        button.pack(side='left', padx=5, pady=0, ipadx=10, ipady=10, anchor='ne')

    def addShareButton(self, buttons_frame):
        share_button = tk.Menubutton(buttons_frame, compound='left', cursor="hand2",
                                     image=ImageHelp.getIcons('more', (18, 18)), activebackground=bg_active,
                                     bg=bg, fg=COLOR2, activeforeground=COLOR1)
        share_menu = tk.Menu(share_button, font=('Open Sans', 10), activeborderwidth=8,
                             activebackground=bg_active, activeforeground=COLOR3,
                             bd=0, bg=bg, fg=COLOR1, tearoff=False)
        share_menu.add_command(label="Save to Picture", command=ImageHelp.capture)
        share_menu.add_command(label="Copy to Text", command=self.copyText)
        share_button.config(menu=share_menu)
        share_button.pack(side='left', padx=0, pady=0, ipadx=10, ipady=10)

    def addTwtterButton(self, buttons_frame):
        button = tk.Menubutton(buttons_frame, compound='left', cursor="hand2",
                                          image=ImageHelp.getIcons('twtter', (18, 18)),
                                          activebackground=bg_active, bg=bg, fg=COLOR2)
        button.bind("<ButtonRelease-1>", lambda e: webbrowser.open(TWTTER_URL, new=0, autoraise=True))
        button.pack(side='left', padx=0, pady=0, ipadx=10, ipady=10, anchor='ne')

    def addInstagramButton(self, buttons_frame):
        button = tk.Menubutton(buttons_frame, compound='left', cursor="hand2",
                                          image=ImageHelp.getIcons('instagram', (18, 18)),
                                          activebackground=bg_active, bg=bg, fg=COLOR2)
        button.bind("<ButtonRelease-1>", lambda e: webbrowser.open(INSTAGRAM_URL, new=0, autoraise=True))
        button.pack(side='left', padx=0, pady=0, ipadx=10, ipady=10, anchor='ne')

    def addFaqButton(self, buttons_frame):
        button = tk.Menubutton(buttons_frame, compound='left', cursor="hand2",
                                          image=ImageHelp.getIcons('faq', (18, 18)),
                                          activebackground=bg_active, bg=bg, fg=COLOR2)
        button.bind("<ButtonRelease-1>", lambda e: webbrowser.open(FAQ_URL, new=0, autoraise=True))
        button.pack(side='left', padx=0, pady=0, ipadx=10, ipady=10, anchor='ne')

    def addGuideButton(self, buttons_frame):
        button = tk.Menubutton(buttons_frame, compound='left', cursor="hand2",
                                          image=ImageHelp.getIcons('guide', (18, 18)),
                                          activebackground=bg_active, bg=bg, fg=COLOR2)
        button.bind("<ButtonRelease-1>", lambda e: webbrowser.open(GUIDE_URL, new=0, autoraise=True))
        button.pack(side='left', padx=0, pady=0, ipadx=10, ipady=10, anchor='ne')

    def addCloseButton(self, buttons_frame):
        button = tk.Menubutton(buttons_frame, compound='left', cursor="hand2", image=ImageHelp.getIcons('close', (18, 18)),
                               activebackground="#d40000", bg="#a60808", fg=COLOR2)
        button.bind("<ButtonRelease-1>", lambda e: self.quit())
        button.pack(side='left', padx=0, pady=0, ipadx=10, ipady=10, anchor='ne')

    def initScoreFrame(self):
        frame = tk.Frame(self, bg=bg)
        frame.pack(side='top', padx=20, pady=10, fill='x')
        self.global_score_frame = GlobalScoreFrame(frame, self.hardware, self.benchmark["score"])
        self.cpu_score_frame = ScoreFrame(frame, "CPU", self.benchmark["cpu"])
        self.gpu_score_frame = ScoreFrame(frame, "Graph Card", self.benchmark["gpu"])
        self.mem_score_frame = ScoreFrame(frame, "Memory", self.benchmark["memory"])
        self.disk_score_frame = ScoreFrame(frame, "Disk", self.benchmark["disk"])

    def initHardwareFrame(self):
        frame = tk.Frame(self, bg=bg)
        frame1 = tk.Frame(frame, bg=bg)
        self.initItemFrame(frame1, 'cpu', 'CPU', self.hardware['cpu'])
        self.initItemFrame(frame1, 'disk', 'Storage', self.hardware['disk'])
        self.initItemFrame(frame1, 'memory', 'Physical Memory', self.hardware['memory'])
        self.initItemFrame(frame1, 'network', 'Network Adapter', self.hardware['network'], False)
        frame1.grid(row=0, column=0, sticky="nsew", padx=20, pady=10)

        frame2 = tk.Frame(frame, bg=bg)
        self.initItemFrame(frame2, 'gpu', 'Video Cards', self.hardware['gpu'])
        self.initItemFrame(frame2, 'display', 'DISPLAY', self.hardware['display'])
        self.initItemFrame(frame2, 'sound', 'Sound Media', self.hardware['sound'] + self.hardware['media'])
        self.initItemFrame(frame2, 'keyboard', 'Keyboard', [self.hardware['keyboard']])
        self.initItemFrame(frame2, 'mouse', 'Mouse', [self.hardware['mouse']], False)
        frame2.grid(row=0, column=1, sticky="nsew", padx=20, pady=10)
        frame.grid_columnconfigure(0, weight=1)
        frame.grid_columnconfigure(1, weight=1)
        frame.pack(side='top', fill='both')

    def initItemFrame(self, parent, icon, title, datas=[], hasLine=True):
        if datas is None:
            return
        frame = tk.Frame(parent, bg=bg)
        tk.Label(frame, image=ImageHelp.getIcons(icon), width=28, bg=bg, fg=COLOR1).grid(row=0, column=0, padx=5)
        tk.Label(frame, text=title, bg=bg, fg=COLOR1, font=('Open Sans', 9, 'bold')).grid(row=0, column=1, padx=5, sticky='w')
        for index, data in enumerate(datas):
            tk.Label(frame, text=data['name'], bg=bg, fg=COLOR3, font=('Open Sans', 10)).grid(row=index + 1,
                                                                                                        column=1,
                                                                                                        padx=5,
                                                                                                        sticky='w')
        frame.pack(side='top', padx=10, pady=5, fill='both')
        if hasLine:
            self.initLine(parent)

    def initLine(self, parent):
        line = tk.Frame(parent, bg=bg_line, height=1)
        line.propagate(0)
        line.pack(side='top', pady=5, fill='both')

    def copyText(self):
        text = self.hardware['system']['name'] + "(" + self.hardware['system']['Model'] + ")\r\n\r\n"
        text = text + "CPU\r\n"
        for item in self.hardware['cpu']:
            text = text + "  |-->" + item['name'] + "\r\n"
        text = text + "Video Cards\r\n"
        for item in self.hardware['gpu']:
            text = text + "  |-->" + item['name'] + "\r\n"
        text = text + "Physical Memory\r\n"
        for item in self.hardware['memory']:
            text = text + "  |-->" + item['name'] + "\r\n"
        text = text + "Storage\r\n"
        for item in self.hardware['disk']:
            text = text + "  |-->" + item['name'] + "\r\n"
        text = text + "Display\r\n"
        for item in self.hardware['display']:
            text = text + "  |-->" + item['name'] + "\r\n"
        text = text + "Sound Media\r\n"
        for item in self.hardware['sound'] + self.hardware['media']:
            text = text + "  |-->" + item['name'] + "\r\n"
        text = text + "Network Adapter\r\n"
        for item in self.hardware['network']:
            text = text + "  |-->" + item['name'] + "\r\n"
        text = text + "Keyboard\r\n"
        for item in [self.hardware['keyboard']]:
            text = text + "  |-->" + item['name'] + "\r\n"
        text = text + "Mouse\r\n"
        for item in [self.hardware['mouse']]:
            text = text + "  |-->" + item['name'] + "\r\n"
        pyperclip.copy(text)
        messagebox.showinfo('message', 'copy success')

    def findScore(self):
        def loadBenchmark(hardware, updateScore):
            logging.info("start benchmark")
            benchmark = Benchmark.all()
            logging.info(benchmark)
            updateScore(benchmark)
            uploadHardwareScore(hardware, benchmark, updateScore)
            if benchmark['has_error']:
                api.report(benchmark)

        def uploadHardwareScore(hardware, benchmark, updateScore):
            logging.info("update benchmark")
            try:
                score = api.getHardwareScore(hardware, benchmark)
                logging.info(score)
                if score is not None:
                    updateScore(score)
                return
            except Exception as e:
                logging.error("report benchmark error", e)
            updateScore(benchmark)

        t = threading.Thread(target=loadBenchmark, args=(self.hardware, self.updateScore))
        t.setDaemon(True)
        t.start()

    def updateScore(self, benchmark):
        self.global_score_frame.updateScore(benchmark['score'])
        self.cpu_score_frame.updateScore(benchmark['cpu'])
        self.gpu_score_frame.updateScore(benchmark['gpu'])
        self.mem_score_frame.updateScore(benchmark['memory'])
        self.disk_score_frame.updateScore(benchmark['disk'])

    def versionCheck(self):
        self.version = VersionUtil.getVersion()

        def check(version, shoudUpdate):
            logging.info("check version for update:" + version)
            new_version = api.getVersion('ABS' if PRODUCT_ABS_TAG else 'DEFAULT')
            logging.info(new_version)
            if new_version and VersionUtil.compare(version, new_version['version']) < 0:
                shoudUpdate(new_version)

        t = threading.Thread(target=check, args=(self.version, Application.shoudUpdate))
        t.setDaemon(True)
        t.start()

    def showProcudtKey(self):
        try:
            pyperclip.copy(self.hardware['productkey']['name'])
            messagebox.showinfo('windows product key', self.hardware['productkey']['name'] + "\n(It's already copied to the clipboard for you to paste.)")
        except Exception as e:
            messagebox.showinfo('windows product key', "this computer has no product key")

    @staticmethod
    def shoudUpdate(version):
        if messagebox.askyesno('message', 'find new version:' + version['version']):
            webbrowser.open(version['url'], new=0, autoraise=True)


class GlobalScoreFrame:
    def __init__(self, parent, hardware, data):
        name = hardware['system']['Manufacturer'] + " " + hardware['system']['SystemFamily']
        if len(name) > 30:
            name = hardware['baseboard']['Manufacturer'] + " " + hardware['baseboard']['Product']
        if "," in name:
            name = hardware['baseboard']['Manufacturer'] + " " + hardware['baseboard']['Product']
        all_score_frame = tk.Frame(parent, width=230, height=130, bg=bg)
        self.all_score_label = tk.Label(all_score_frame, text=data, bg=bg, cursor="hand2", fg=COLOR3, font=('Open Sans', 25, 'bold'))
        self.all_score_label.pack(side='top', anchor='w')
        tk.Label(all_score_frame, text='DEVICE POINTS', bg=bg, fg=COLOR1, font=('Open Sans', 9, 'bold')).pack(side='top', anchor='w')
        title_frame = tk.Frame(all_score_frame, bg=bg)
        tk.Label(title_frame, text=name, bg=bg, fg=COLOR3, font=('Open Sans', 10, 'bold')).pack(side='top',anchor='sw')
        tk.Label(title_frame, text=hardware['system']['Model'], bg=bg, fg=COLOR3, font=('Open Sans', 9)).pack(side='top',anchor='sw')
        title_frame.pack(side='bottom', pady=5, anchor='sw')
        all_score_frame.propagate(0)
        all_score_frame.pack(side='left', padx=5, anchor='nw')

    def updateScore(self, score):
        self.all_score_label.config(text=str(score))
        self.all_score_label.update()


class ScoreFrame:
    frame_style = {"bg": bg_active, "width": 130, "height": 130, "highlightthickness": 1, "highlightbackground": COLOR1}
    label1_style = lambda self, name: {"text": name, "bg": bg_active, "fg": COLOR1, "font": ('Open Sans', 12, 'bold')}
    label2_style = {"text": "Score", "bg": bg_active, "fg": COLOR1, "font": ('Open Sans', 10)}
    label3_style = lambda self, name: {"text": name, "bg": bg_active, "fg": COLOR3, "font": ('Open Sans', 16, 'bold')}
    label3_loading_style = {"text": 'loading...', "bg": bg_active, "fg": COLOR1, "font": ('Open Sans', 10, 'bold')}

    frame_pack = {"side": 'left', "padx": 8}
    label1_pack = {"side": 'top', "ipadx": 10, "ipady": 15, "anchor": 'w'}
    label2_pack = {"side": 'top', "ipadx": 10, "anchor": 'w'}
    label3_pack = {"side": 'top', "ipadx": 10, "anchor": 'w'}

    def __init__(self, parent, label, data):
        super().__init__()
        score_frame = tk.Frame(parent, self.frame_style)
        tk.Label(score_frame, self.label1_style(label)).pack(self.label1_pack)
        self.label = tk.Label(score_frame, self.label2_style)
        self.label.pack(self.label2_pack)
        if data is not None:
            self.score = tk.Label(score_frame, self.label3_style(data))
        else:
            self.score = tk.Label(score_frame, self.label3_loading_style)
        self.score.pack(self.label3_pack)
        score_frame.propagate(0)
        score_frame.pack(self.frame_pack)

    def updateScore(self, score):
        if score['star'] and score['star'] > 0:
            if score['star'] > 5:
                score['star'] = 5
            self.label.config(text=None, image=ImageHelp.getIcons(str(score['star']), (50, 10)))
            self.label.pack({"side": 'top', "ipadx": 10, "ipady": 5, "anchor": 'w'})
            self.score.update()
        if score is not None:
            self.score.config(text=str(score['score']), fg=COLOR3, font=('Open Sans', 16, 'bold'))
        else:
            self.score.config(text=str(score['score']), fg=COLOR1, font=('Open Sans', 10, 'bold'))
        self.score.update()


if __name__ == "__main__":
    if sys.argv[0] and sys.argv[0].endswith('.debug.exe'):
        logging.basicConfig(level=logging.DEBUG, filename='eniac.log', filemode='a', format='%(asctime)s - %(message)s')
    else:
        logging.basicConfig(format='%(asctime)s - %(message)s', level=logging.INFO)
    try:
        app = Application()
        app.mainloop()
    except Exception as e:
        api.report({'error': traceback.extract_tb(sys.exc_info()[2]), 'message': str(sys.exc_info()[0:2])})
        logging.error(e)
        messagebox.showerror('Error', e)
        raise e
